package com.sjf.JavaNIO.buffer;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author <huangtang>
 * @since 2022/4/14 11:18
 **/
public class BufferDemo2 {

    static final int start = 0;
    static final int size = 1024;

    /**
     * 内存映射文件IO
     * 只会将实际读取或写入的数据映射到文件中，不会将整块连续数组放进内存中，所以效率很高
     */
    public void MemoryMappingFileIO () throws Exception {
        RandomAccessFile aFile = new RandomAccessFile ("D:\\atguigu\01.txt" , "rw");
        FileChannel channel = aFile.getChannel ();
        MappedByteBuffer mbb = channel.map (FileChannel.MapMode.READ_WRITE , start , size);

        mbb.put (0 , (byte) 97);
        mbb.put (1023 , (byte) 122);
        aFile.close ();
    }

    /**
     * 直接缓冲区
     * 通过在两个通道中直接建立起联系的缓冲区，不需要和I/O服务打交道，直接和通道进行通信，效率较高
     */
    @Test
    public void DirectBuffer () throws Exception {
        String infile = "D:\\atguigu\\01.txt";
        FileInputStream fis = new FileInputStream (infile);
        //文件输入流的通道
        FileChannel fisChannel = fis.getChannel ();

        String outfile = "D:\\atguigu\\02.txt";
        FileOutputStream fos = new FileOutputStream (outfile);
        //文件输出流的通道
        FileChannel fosChannel = fos.getChannel ();

        //创建直接缓冲区
        ByteBuffer buffer = ByteBuffer.allocateDirect (1024);

        while (true) {
            buffer.clear ();
            //将fis通道的内容写入缓冲区中
            int readCount = fisChannel.read (buffer);
            if ( readCount == -1 ) {
                break;
            }
            buffer.flip ();//重置指针
            //fos从缓冲区读取内容
            fosChannel.write (buffer);
        }
    }

    /**
     * 只读缓冲区和原缓冲区的hb对象地址是一样的，只不过只读缓冲区只能读取，不能写入
     */
    @Test
    public void ReadOnlyBuffer () {
        //创建一个Buffer
        ByteBuffer buffer = ByteBuffer.allocate (10);
        //向buffer中放入数据
        for ( int i = 0 ; i < buffer.capacity () ; i++ ) {
            buffer.put ((byte) i);
        }

        //创建一个只读缓冲区
        ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer ();

        for ( int i = 0 ; i < buffer.capacity () ; i++ ) {
            byte b = buffer.get (i);
            b *= 10;
            buffer.put (i , b);
        }

        readOnlyBuffer.position (0);
        readOnlyBuffer.limit (buffer.capacity ());

        while (readOnlyBuffer.hasRemaining ()) {
            System.out.println (readOnlyBuffer.get ());
        }

    }

    /**
     * 缓冲区分片
     * 缓冲区分片又叫子缓冲区，子缓冲区的hb对象地址和父缓冲区一样，数据是共享的
     */
    @Test
    public void BufferSlice () {
        //创建一个Buffer
        ByteBuffer buffer = ByteBuffer.allocate (10);
        //向buffer中放入数据
        for ( int i = 0 ; i < buffer.capacity () ; i++ ) {
            buffer.put ((byte) i);
        }

        //创建子缓冲区
        buffer.position (3); //设置子缓冲区的开始指针位置
        buffer.limit (7);

        ByteBuffer slice = buffer.slice ();

        //改变子缓冲区写入内容
        for ( int i = 0 ; i < slice.capacity () ; i++ ) {
            byte b = slice.get (i);
            b *= 10;
            slice.put (i , b);
        }

        //中途没有对buffer进行操作完，所以对position和limit手动设置
        //如果使用flip方法会导致buffer数据读取不完整
        buffer.position (0);
        buffer.limit (buffer.capacity ());

        while (buffer.hasRemaining ()) {
            System.out.println (buffer.get ());
        }
    }
}
